Skip to content

Generate a static module entrypoint behind a staticEntrypoint setting - #28

Merged
eunomie merged 12 commits into
dagger:mainfrom
eunomie:python-sdk-static-entrypoint-lead-pythonsdk-080b6d74
Sep 15, 2026
Merged

eunomie merged 12 commits into
dagger:mainfrom
eunomie:python-sdk-static-entrypoint-lead-pythonsdk-080b6d74

Conversation

@eunomie

@eunomie eunomie commented Sep 12, 2026

Copy link
Copy Markdown
Member

Static module entrypoint (opt-in)

With the SDK setting staticEntrypoint = true, dagger generate writes a manifest version 2 module whose types the engine loads without running Python.

How it works

  • dagger generate builds the module's container, imports the module inside it (python -m dagger.mod entrypoint) and renders what its decorators registered into sdk/entrypoint/types.dang. The types are the ones the runtime would register, from the same code path (Module.describe() feeds both to_typedef and the renderer).
  • sdk/entrypoint/main.dang implements ModuleEntrypoint: types() returns the literal TypeDef list; call() runs python -m dagger.mod call in the module's container, which dispatches through the existing registry. A per-file content digest of every source file that can change the types guards against a stale entrypoint: a call after an edit is refused with a message to run dagger generate.
  • sdk/entrypoint/build.dang is a copy of the new runtime/build.dang (the container build, split out of runtime/main.dang behind type PythonModuleBuild) with the image pins inlined, so the entrypoint needs nothing but itself.
  • The manifest becomes the five lines version 2 allows. The dynamic runtime stays the default; a module switches either way with dagger module init python --path <module> --static-entrypoint[=false] and dagger generate.

What version 2 cannot carry is refused at dagger generate instead of dropped: module clients, cache= on a function, the legacy template, include, a source other than ., disableDefaultFunctionCaching, another runtime, and the codegen, clients or dependencies tables.

Design and history in future/static-module-entrypoint.md, including why the types come from importing the module rather than a static analyzer (dagger/dagger#11803 and its removal in #13251).

Testing

  • sdk/tests/mod: test_describe.py (parity with the previous to_typedef decisions), test_entrypoint.py (goldens for the rendered Dang, digests in the engine's File.digest(excludeMetadata: true) format, the entrypoint subcommand), test_dispatch.py (the call subcommand).
  • e2e: static-scope-init, static-scope-switch, static-scope-refusals, static-types-load next to the existing checks; all green locally on the CI engine.
  • On a dev engine with the version 2 loader: dagger functions on a generated module takes 1.2 s against 2.9 s on the dynamic path; calls run end to end; an edited or added source file, or a lock file added later, is refused naming the file; a .venv/ or a permission-only change is not.

CI's engine does not load version 2 yet, so the e2e checks prove generation and refusals; loading is covered by the dev-engine run above.

Follow-ups

  • Flip the default once engines with the version 2 loader are released (phase 2 of the rollout in the design doc).
  • Module clients on the static path, when version 2 gains a dependency model.

Generate a manifest v2 entrypoint whose types() is written at
dagger generate time, by importing the module in its own container
and reading the runtime registry, gated by a staticEntrypoint SDK
setting rolled out in two phases with the dynamic path kept.

Signed-off-by: Yves Brissaud <yves@dagger.io>
Module.describe() walks the registry into plain dataclasses, and
_typedefs() and to_typedef() materialise them into API calls. A
static entrypoint renders the same description at generate time.

Signed-off-by: Yves Brissaud <yves@dagger.io>
python -m dagger.mod entrypoint imports the module, describes it and
writes types.dang (a literal TypeDef list) and main.dang (the
ModuleEntrypoint that calls the module's container). main.dang bakes
the content digests of the module's sources, so a stale entrypoint
refuses to run instead of serving wrong types.

Signed-off-by: Yves Brissaud <yves@dagger.io>
python -m dagger.mod call reads the request a ModuleEntrypoint forwards,
runs it through the registry and writes the JSON result to a file, so
the generated entrypoint never depends on stdout.

Signed-off-by: Yves Brissaud <yves@dagger.io>
PythonModuleBuild in runtime/build.dang builds a module's container from
its committed files; the runtime module adds runtime.py on top of it, and
the generated static entrypoint carries a copy of the same build.

Signed-off-by: Yves Brissaud <yves@dagger.io>
…setting

With staticEntrypoint = true, `dagger generate` builds the module's
container, renders its types into sdk/entrypoint/types.dang and writes a
manifest version 2 that points at that entrypoint. The dynamic runtime
stays the default and a module switches back by turning the setting off.

Settings a version 2 manifest cannot carry (module clients, include,
disableDefaultFunctionCaching, another runtime) are refused instead of
silently dropped.

Signed-off-by: Yves Brissaud <yves@dagger.io>
Signed-off-by: Yves Brissaud <yves@dagger.io>
The static path is in the repository, green in CI, and loads on a dev
engine with the version 2 loader. Flipping the default waits for a
released engine.

Signed-off-by: Yves Brissaud <yves@dagger.io>
@eunomie
eunomie force-pushed the python-sdk-static-entrypoint-lead-pythonsdk-080b6d74 branch from 339bff8 to 097a3fc Compare September 15, 2026 20:08
Signed-off-by: Yves Brissaud <yves@dagger.io>
@eunomie
eunomie force-pushed the python-sdk-static-entrypoint-lead-pythonsdk-080b6d74 branch from 097a3fc to 7bfca7b Compare September 15, 2026 20:10
@eunomie
eunomie marked this pull request as ready for review September 15, 2026 20:11
7bfca7b made staticEntrypoint a private field of PythonSdk, so
pythonSdk(staticEntrypoint: true) no longer type-checks. The e2e module
failed to load and every e2e check failed with it, which broke
engine-e2e:dev-sdk-check.

Remove the four checks that turn the setting on, and the static-types
fixture only they used. Revert this together with 7bfca7b once the
engine loads manifest version 2 and the setting is public again.

Signed-off-by: Yves Brissaud <yves@dagger.io>
The staticEntrypoint setting is private until the engine loads manifest
version 2, so drop the --static-entrypoint instructions and describe
what generating an existing static module does meanwhile. Name the
engine the dev-sdk check builds.

Signed-off-by: Yves Brissaud <yves@dagger.io>
Pin the dev-sdk check's engine and its engine-dev dependency to
6bf59d50, the v1.0.0-beta.13 tag, instead of 0d031c08. The lock gains
the pins the engine build resolves.

Signed-off-by: Yves Brissaud <yves@dagger.io>
@eunomie

eunomie commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

Public flag is dropped, the feature is there but not available, so mergable with good confidence.

@eunomie
eunomie merged commit 290ed4c into dagger:main Sep 15, 2026
2 checks passed
eunomie added a commit that referenced this pull request Sep 16, 2026
Reverts the three commits that made the static entrypoint private
before merging PR #28, so the feature can be tested by hand against a
dev engine with manifest version 2 support (dagger/dagger#14038):

Revert "docs: say the static entrypoint is not available yet"
Revert "e2e: drop the static entrypoint checks while the setting is private"
Revert "temporary remove the static-entrypoint public var"

dagger.lock is left as it is on main: the lock churn in the last commit
was incidental regeneration, not part of making the setting private.

Signed-off-by: Yves Brissaud <yves@dagger.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant